
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>SpringBoot中实现动态数据源切换 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>SpringBoot中实现动态数据源切换 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">什么是动态数据源切换的核心思想？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">动态数据源切换的核心思想是，根据不同的业务场景、请求类型、用户身份或者负载情况，动态地切换到不同的数据源。</div>
          </div>
          <div class="card-source">来源: 基础知识 -> 2. 动态数据源切换的概念</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">文档中提到了哪三种动态数据源切换的典型应用场景？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">1. 多租户架构：每个租户使用独立的数据库。 2. 读写分离：主库用于写操作，从库用于读操作。 3. 数据库分片：将数据分散到多个数据库以应对海量数据。</div>
          </div>
          <div class="card-source">来源: 基础知识 -> 2. 动态数据源切换的概念</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在Spring框架中，实现数据源动态路由的核心抽象类是什么？它的关键作用是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">核心抽象类是 `AbstractRoutingDataSource`。它的作用是通过重写 `determineCurrentLookupKey()` 方法，在运行时根据特定条件（如线程上下文中的标识）返回一个查找键，从而动态地选择并切换到目标数据源。</div>
          </div>
          <div class="card-source">来源: 设计思路 -> 4.1 创建动态数据源路由</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在实现动态数据源切换时，通常使用什么机制来管理当前线程的数据库标识，以确保线程安全？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通常通过 `ThreadLocal` 来管理当前线程的数据库标识。这可以确保每个线程都有独立的数据源上下文，避免在并发环境下发生数据源混淆的问题。文档中的 `DataSourceContextHolder` 类就是这一机制的实现。</div>
          </div>
          <div class="card-source">来源: 设计思路 -> 4.2 数据源上下文管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">挑战</div>
          <div class="card-question">实现动态数据源切换时面临的主要挑战有哪些？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">挑战</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要挑战包括三个方面：1. 事务的隔离性，确保切换过程中事务的原子性和一致性；2. 数据源的管理，如何高效管理和切换多个数据源的连接池；3. 性能问题，避免频繁切换数据源导致性能瓶颈。</div>
          </div>
          <div class="card-source">来源: 基础知识 -> 4. 动态数据源的挑战</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">如何使用AOP和自定义注解实现一种优雅的数据源切换策略？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">通过创建一个自定义注解（如 `@TargetDataSource`）来标记需要切换数据源的业务方法。然后，编写一个AOP切面（Aspect），拦截所有被该注解标记的方法。在方法执行前，切面根据注解的值设置当前线程的数据源上下文；在方法执行后，清除该上下文，从而实现非侵入式的动态数据源切换。</div>
          </div>
          <div class="card-source">来源: AOP实现数据源切换 -> 3. 实现步骤</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">策略</div>
          <div class="card-question">在读写分离场景中，最常见的数据源切换策略是怎样的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">策略</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">最常见的策略是基于操作类型进行切换：所有的写操作（如Insert, Update, Delete）都访问主数据库，而所有的读操作（Select）则访问从数据库，以此来分担主库的压力。</div>
          </div>
          <div class="card-source">来源: 切换策略 -> 1. 读写分离策略</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实践</div>
          <div class="card-question">为什么在每次数据库操作完成后，必须清理当前线程的数据源上下文（例如调用`DataSourceContextHolder.clearDataSourceType()`）？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实践</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为Web服务器的线程是会被复用的，如果不清理当前线程的数据源标识，后续的请求可能会错误地沿用上一个请求设置的数据源，导致数据访问混乱或出错。因此，操作完成后清理上下文是避免该问题的关键步骤。</div>
          </div>
          <div class="card-source">来源: AOP实现数据源切换 -> 3) 编写切面</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">当业务操作需要跨越多个数据库并保证事务一致性时，应该采用什么技术方案？文档中提到了哪些具体框架？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">当涉及跨多个数据库的事务时，应使用分布式事务框架来管理。这些框架可以确保在一个数据源操作失败时，所有相关数据源的操作都会回滚。文档中提到的框架有 Atomikos 和 Seata。</div>
          </div>
          <div class="card-source">来源: 异常与回滚处理 -> 5) 跨数据库事务管理（分布式事务）</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
